package com.df.excel_export.common;

import com.df.excel_export.excel_table.ExcelTableStructure;
import com.df.excel_export.project_config.ProjectConfig;
import com.df.excel_export.graph.OrthogonalGraph;
import com.df.excel_export.utils.ExcelTools;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 1.数据依赖检查：只需要解析表头和校验字符串即可
 * 2.数据校验：从依赖树的根节点(根节点一般为前段选中的查询表)，层序遍历校验，校验后将数据移除，避免内存泄漏
 * 3.数据序列化和代码生成，按指定顺序依次执行即可
 */
@Data
public class ExportTask extends Thread {
    public static final Logger logger = LoggerFactory.getLogger(ExportTask.class);
    public static ExportTask nowTask = null;


    private String environment = "default";//分组当前处理的环境

    private List<String> tableNames;//前端传过来需要处理的表绝对路径


    //项目配置
    public ProjectConfig projectConfig;
    //任务类型
    public int taskType = 0;
    boolean canContinue = true;//是否可以继续后面的任务


    public static final int TaskType_ParseDepends = 1;//检查依赖
    public static final int TaskType_DataCheck = 1<<1;//数据检查(包含检查依赖)
    public static final int TaskType_Serialize = 1<<2;//表数据序列化
    public static final int TaskType_GeneratorCode = 1<<3;//代码生成(表实体，枚举，表数据管理器)


    @Override
    public void run() {
        try {
            doTask();
        } catch (Exception e){
            if(e instanceof InterruptedException){
                logger.warn("任务被中断！");
            }else {
                ExcelTools.addExceptionMsg(e);
            }
        }finally {
            ExportTask.nowTask = null;
            ExcelTools.respTaskFinish();
        }
    }

    public static boolean canContinue(){
        if(nowTask==null){
            return false;
        }
        return !nowTask.isInterrupted();
    }

    private void doTask() {
        //TODO 当前任务能被其他线程中止？能的话使用 interrupt+isInterrupted 来判断是否继续执行，注意是否要清除标识
        if((taskType & TaskType_DataCheck)==1){
            taskType |=TaskType_ParseDepends;
        }

        //1.先根据excel文件夹路径，解析所有表的信息
        logger.info("解析配置文件元数据中。。。");
        parseProjectAllTableMeta();
        logger.info("解析配置文件元数据完成!数据表数量："+ExcelTableManager.projectExcelMetaCaches.get(projectConfig.getName()).size());

        if(!canContinue()){
            return;
        }
        //2.解析所有表的枚举
        logger.info("解析配置枚举数据中...");
        parseProjectAllEnumMeta();
        logger.info("解析配置枚举数据完成！枚举数量："+ExcelTableManager.projectEnums.get(projectConfig.getName()).getEnums().size());



        if((taskType & TaskType_ParseDepends) == 1){//依赖检查
            if(!canContinue()){
                return;
            }
            logger.info("解析依赖中....");
            parseTableDepends();
            logger.info("解析依赖完成！");
        }
        if((taskType & TaskType_DataCheck) == 1){//数据检查
            if(!canContinue()){
                return;
            }
            dataCheck();
        }
        //TODO 这些交给插件来完成
        if((taskType & TaskType_Serialize) == 1){//序列化
            if(!canContinue()){
                return;
            }
            dataSerialize();
        }
        if((taskType & TaskType_GeneratorCode) == 1){//代码生成
            if(!canContinue()){
                return;
            }
            generatorCode();
        }

        //按拓扑排序依次操作：读取所需数据(检查表的数据和依赖表的数据)，检查完后按清空序列化数据和生成代码，然后卸载不会再用到的数据(缓存到文件，防止内存数据过大oom)
    }

    private void parseProjectAllEnumMeta() {
        if(projectConfig.getEnum_path()==null || projectConfig.getEnum_path().isEmpty()){
            logger.warn("项目枚举配置文件未指定，放弃解析！");
            return;
        }
        ProjectEnums enums = ExcelTableManager.getProjectEnums(projectConfig.getEnum_path());

        //获取枚举文件
        File enumFile = new File(projectConfig.getEnum_path());

        if(enums==null){
            parseProjectEnum(projectConfig.getEnum_path());
            return;
        }
        if(enums.getLastModify().equals(enumFile.lastModified())){
            return;
        }

        parseProjectEnum(projectConfig.getEnum_path());
    }

    private void parseProjectEnum(String enumPath) {

        ExcelTools.parseTableEnum(enumPath,projectConfig);
    }

    public void parseProjectAllTableMeta() {
        boolean change = false;
        List<File> files = new ArrayList<>();
        //递归获取文件夹下的所有文件
        try {
            Files.walkFileTree(Paths.get(projectConfig.getExcel_path()),new FileVisitor<Path>() {

                @Override
                public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                    return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                    if(file.toAbsolutePath().equals(projectConfig.getEnum_path())){
                        return FileVisitResult.CONTINUE;
                    }
                    files.add(file.toFile());
                    return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
                    return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                    return FileVisitResult.CONTINUE;
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
        List<File> parseFiles = files.stream().filter(f -> {
            if (!f.getName().endsWith(".xlsx") && !f.getName().endsWith(".xls")) {
                return false;
            }
            if (f.getName().endsWith("!.xlsx") || f.getName().endsWith("!.xls")) {
                return false;
            }
            if (f.getName().startsWith("~$")) {
                return false;
            }
            return true;
        }).collect(Collectors.toList());
        List<String> fileNames = parseFiles.stream().map(f -> f.getAbsolutePath()).collect(Collectors.toList());
        //获取项目的缓存
        Map<String, ExcelTableStructure> excelTableStructureMap = ExcelTableManager.getProjectExcelMeta(projectConfig.getName());
        if(excelTableStructureMap==null){
            excelTableStructureMap = new HashMap<>();
            ExcelTableManager.projectExcelMetaCaches.put(projectConfig.getName(),excelTableStructureMap);
            //全部解析
            List<ExcelTableStructure> result = parseProjectTableHeader(fileNames);
            if(ExportTask.canContinue()){
                for (ExcelTableStructure structure : result) {
                    if(structure.isParserDone()){
                        excelTableStructureMap.put(structure.getMeta().getFilePath(), structure);
                    }
                }

            }

            change = true;
        }else {
            
            Set<String> oldFileNames = excelTableStructureMap.keySet();
            //新增
            List<String> addTemp = new ArrayList<>();
            addTemp.addAll(fileNames);
            addTemp.removeAll(oldFileNames);

            //减少
            List<String> removeTemp = new ArrayList<>();
            removeTemp.addAll(oldFileNames);
            removeTemp.removeAll(fileNames);

            if(!removeTemp.isEmpty()){
                for (String s : removeTemp) {
                    excelTableStructureMap.remove(s) ;
                }
            }
            List<String> changedFiles = new ArrayList<>();
            //修改
            for (Map.Entry<String, ExcelTableStructure> entry : excelTableStructureMap.entrySet()) {
                File file = new File(entry.getKey());
                long lastModified = file.lastModified();
                long lastModifyTime = entry.getValue().getMeta().getLastModifyTime();
                if(lastModified!=lastModifyTime){
                    changedFiles.add(entry.getKey());
                }
            }
            changedFiles.addAll(addTemp);

            if(!changedFiles.isEmpty()){
                //扫描文件的修改时间和增删情况，筛选出改变过的，重新解析
                List<ExcelTableStructure> result = parseProjectTableHeader(changedFiles);
                if(ExportTask.canContinue()){
                    for (ExcelTableStructure structure : result) {
                        if(structure.isParserDone()){
                            excelTableStructureMap.put(structure.getMeta().getFilePath(), structure);
                        }
                    }
                }

            }
            if(ExportTask.canContinue() && (!changedFiles.isEmpty() || !removeTemp.isEmpty())){
                change = true;
            }

        }
        if(change){
            ExcelTableManager.saveProjectExcelMeta(projectConfig.getName(),excelTableStructureMap);
        }
    }

    /**
     * 解析excel的相关数据
     *
     * @param files
     * @return
     */
    private List<ExcelTableStructure> parseProjectTableHeader(List<String> files) {
        List<ExcelTableStructure> list = new ArrayList<>();
        List<File> parseFiles = files.stream().map(f -> new File(f)).toList();
        try {
            for (File parseFile : parseFiles) {
                if(!canContinue()){
                    return list;
                }
                String name = parseFile.getName();
                int index = name.lastIndexOf(".");
                name = name.substring(0, index);

                ExcelTableStructure structure = ExcelTools.parseTableHeader(parseFile, projectConfig);
                if(structure!=null && structure.isParserDone()){
                    list.add(structure);
                }
                if(!ExportTask.canContinue()){
                    break;
                }
            }
        }catch (Exception e){
            ExportTask.nowTask.interrupt();
            ExportTask.logger.error(e.getMessage());
            e.printStackTrace();
        }

        return list;
    }

    //TODO 序列化，代码生成前先检查必要配置是否都配置了
    private void baseConfigAndGroupConfigCheck() {
        //项目基础配置
        //分组配置

    }

    private void generatorCode() {

    }

    private void dataSerialize() {

    }


    private void parseTableDepends() {
        //解析表依赖
//        List<ExcelTable> toParseRuleTables = new ArrayList<>();
        List<ExcelTableStructure> toParseRuleTables = new ArrayList<>();
        Map<String, ExcelTableStructure> tableStructureMap = ExcelTableManager.projectExcelMetaCaches.get(projectConfig.getName());
        for (String tableName : tableNames) {
            ExcelTableStructure structure = tableStructureMap.get(tableName);
            if(structure!=null && !tableName.equals(projectConfig.getEnum_path())){
                toParseRuleTables.add(structure);
            }
        }
        //解析校验表达式
        ExcelTools.parseTableRule(toParseRuleTables, projectConfig);

        //检查依赖，看是否要继续解析文件夹的其他文件
        if(!canContinue()){
            return;
        }
//        List<String> handleFiles = new ArrayList<>();
//        parseContinuHandleDenpends(handleFiles, toParseRuleTables);
//
//        if(!canContinue){
//            return;
//        }
        graphInitAndSort();
        //TODO 发给前端数据展示依赖关系

    }

    private void graphInitAndSort() {
        Set<ExcelTableStructure> set = new HashSet<>();
        Map<String, ExcelTableStructure> structureMap = ExcelTableManager.projectExcelMetaCaches.get(projectConfig.getName());
        for (String tableName : tableNames) {
            ExcelTableStructure structure = structureMap.get(tableName);
            if (structure != null) {
                set.add(structure);
            }
        }
//        for (ExcelTableStructure excelTable : set) {
//            Set<String> depends = excelTable.getDepends();
//            if (depends.isEmpty()) {
//                continue;
//            }
//            for (String depend : depends) {
//                ExcelTableStructure first = structureMap.values().stream().filter(s -> s.getTable().getTableName().equals(depend)).findFirst().get();
//                if (first != null) {
//                    set.add(first);
//                    continue;
//                }
//            }
//        }
        OrthogonalGraph<String> graph = new OrthogonalGraph<>() ;
        for (ExcelTableStructure excelTable : set) {
            //加顶点
            graph.addVertex(excelTable.getTable().getTableName());
            Set<String> depends = excelTable.getDepends();
            if (!depends.isEmpty()) {
                //加边
                for (String depend : depends) {
                    graph.addEdge(excelTable.getTable().getTableName(), depend);
                }
            }
        }
        graph.checkDependCycleAndSort();
        ExcelTableManager.projectDepends.put(projectConfig.getName(), graph);
    }

    private void dataCheck() {
//        if (graph.cycle) {//依赖自己的不去掉
//            ExportTask.logger.error("依赖有环，无法进行数据校验，请检查配置！");
//            canContinue = false;
//            return;
//        }
//
//        //TODO 数据检查，按顺序执行每个表每个列的数据校验器
//        List<Vertex<ExcelTable>> sorted = graph.sorted;
//        for (Vertex<ExcelTable> excelTableVertex : sorted) {//主表检查
//            ExcelTable toCheckTable = excelTableVertex.data;
//            //TODO 如果表还未读取数据，先读取数据
//            List<List<Object>> datas = toCheckTable.getDatas();
//            List<List<IFieldValidator>> fieldValidators = toCheckTable.getFieldValidators();
//            List<String> fieldNames = toCheckTable.getFieldNames();
//            List<String> fieldTypes = toCheckTable.getFieldTypes();
//            for (int i = 0; i < datas.size(); i++) {//逐行检查
//                List<Object> rowData = datas.get(i);
//                for (int j = 0; j < fieldValidators.size(); j++) {//逐列检查
//                    //TODO 按分组检查
//                    List<IFieldValidator> iFieldValidators = fieldValidators.get(j);
//                    for (IFieldValidator iFieldValidator : iFieldValidators) {
//                        try {
//                            iFieldValidator.validate(toCheckTable, rowData, i, j, fieldNames.get(j), fieldTypes.get(j), rowData.get(j));
//                        } catch (Exception e) {
//                            e.printStackTrace();
//                            //TODO 日志：哪个表，哪行，哪列，那个规则校验错误
//                            ExportTask.logger.error(e.getMessage());
//                        }
//                    }
//                }
//            }
//        }
    }

    private void parseContinuHandleDenpends(List<String> handleFiles, List<ExcelTableStructure> toParseRuleTables) {
        Set<String> depends = new HashSet<>();
        for (ExcelTableStructure toParseRuleTable : toParseRuleTables) {
            depends.addAll(toParseRuleTable.getDepends());
        }
        if (depends.isEmpty()) {
            return;
        }
        Set<String> allNames = new HashSet<>();
        Map<String, ExcelTableStructure> projectExcelMeta = ExcelTableManager.getProjectExcelMeta(projectConfig.getName());
        for (ExcelTableStructure value : projectExcelMeta.values()) {
//            if(!value.getEnums().isEmpty()){
//                for (String s : value.getEnums().keySet()) {
//                    allNames.add("@"+s);
//                }
//                continue;
//            }
            allNames.add(value.getTable().getTableName());
        }

        List<String> r = depends.stream().filter(d -> allNames.contains(d)).toList();
        if (r.isEmpty()) {
            return;
        }

        //TODO 记录所有已加载的表名
//        List<String> allParsedTableNames = new ArrayList<>(ExcelTableManager.tableName2ExcelTable.keySet());
//        allParsedTableNames.addAll(ExcelTableManager.spliteTables.keySet());
//        if (!depends.isEmpty()) {//有依赖的表还未解析
//            List<String> unParsedDependTableNames = depends.stream()
//                    .filter(name -> !name.startsWith("@"))
//                    .filter(name -> !allParsedTableNames.contains(name)).collect(Collectors.toList());
//            if (!unParsedDependTableNames.isEmpty()) {
//                //解析excel文件夹的其他文件，继续解析依赖表格和枚举
//                String excelFolder = baseConfig.getExcelFolder();
//                //遍历文件夹下的所有文件，先过滤掉已经解析过的文件(一个文档只能有一个数据表)，
//                try {
//                    Files.walkFileTree(Paths.get(excelFolder), new SimpleFileVisitor<Path>() {
//                        @Override
//                        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
//                            if (!file.endsWith(".xlsx")) {
//                                return FileVisitResult.CONTINUE;
//                            }
//                            if (ExcelTableManager.file2ExcelTable.contains(file.toAbsolutePath().toString())) {
//                                return FileVisitResult.CONTINUE;
//                            }
//                            handleFiles.add(file.toAbsolutePath().toString());
//                            return FileVisitResult.CONTINUE;
//                        }
//                    });
//                } catch (Exception e) {
//                    e.printStackTrace();
//                    setCanContinue(false);
//                    ExcelTools.addErrorMsg("解析依赖文件时出错：" + e.getMessage());
//                }
//            }
//        }

    }


}

